package lastfm
{
  import flash.events.EventDispatcher;
  import flash.net.URLVariables;
  import lastfm.Auth;
  import lastfm.events.ApiEvent;
  import lastfm.events.RequestEvent;
  import PrivateVars.lastfm;
  import Tools.Debug;

  /**
   * ...
   * @author Roman Marchenko
   */
  public class Api extends EventDispatcher
  {
    public var userData:Object; // User data.

    private var _errorCount:uint;
    private const MAX_ERROR_COUNT:uint = 5;

    public function Api()
    {
      _errorCount = 0;
    }

    ///////////////////////////////////////////////////////////////////////////

    private function onError(event:RequestEvent):void
    {
      _errorCount += 1;
      Debug.log("Api.onError: errorCount=" + _errorCount + ", " + event.xml);
      if (_errorCount < MAX_ERROR_COUNT)
      {
        if (event.target is Request)
        {
          // Try to re-send the request, may be it was just an accident.
          Debug.log("Resending the request...");
          event.target.redo();
          event.stopPropagation();
          return;
        }
      }

      // The request can't be performed.
      var newevent:ApiEvent = new ApiEvent(ApiEvent.ERROR);
      newevent.error = new Array;
      newevent.error["code"] = event.xml.error.@code.toString();
      newevent.error["message"] = event.xml.error;
      var str:String;
      switch ((int)(newevent.error["code"]))
      {
        case 4:
          str = "Неправильное имя пользователя и/или пароль.";
          break;
        default:
          str = "Ошибка соединения с сервером Last.fm. Код ошибки: " + newevent.error["code"]
            + "\n" + newevent.error["message"];
      }
      newevent.error["message"] = str;
      newevent.userData = userData;

      dispatchEvent(newevent);
    }

    ///////////////////////////////////////////////////////////////////////////

    private function onOkCorrection(event:RequestEvent):void
    {
      Debug.log("Api.onOkCorrection: server response: " + event.xml);
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);

      newevent.response = new Object;
      newevent.response["artist"] = event.xml.corrections[0].correction.artist.name.toString();
      newevent.response["url"] = event.xml.corrections[0].correction.artist.url.toString();
      newevent.userData = userData;

      dispatchEvent(newevent);
    }

    ///////////////////////////////////////////////////////////////////////////

    private function onOkSimilar(event:RequestEvent):void
    {
      Debug.log("Api.onOkSimilar: artist: " + event.xml.similarartists.@artist.toString());
      //Debug.log("Api.onOkSimilar: server response: " + event.xml);
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);

      newevent.response = new Object;
      newevent.response["artist"] = event.xml.similarartists.@artist.toString();
      newevent.response["similar"] = new Array;

      var size:int = event.xml.similarartists.artist.length();
      Debug.log("Api.onOkSimilar: similar artists: " + size);

      var i:int;
      var arr:Array = newevent.response["similar"];
      for (i = 0; i < size; ++i)
      {
        var e:XML = event.xml.similarartists.artist[i];
        if (0.30 > e.match)
        {
          // Artists with a match less then 0.2 are not similar,
          // but if number of artists less than 18, then take artists
          // which match is between 0.2 and 0.1.
          if (18 <= arr.length)
          {
            break;
          }

          if (0.1 > e.match)
          {
            break;
          }
        }

        Debug.log("onOkSimilar: similar artist: " + e.name.toString());
        arr.push(e.name.toString());
      }

      Debug.log("onOkSimilar: extracted " + i + " similar artists.");
      //event.xml.corrections[0].correction.artist.url.toString();

      newevent.userData = userData;
      dispatchEvent(newevent);
    }

    ///////////////////////////////////////////////////////////////////////////

    private function onOkArtistSearch(event:RequestEvent):void
    {
      Debug.log("Api.onOkSearch: results: " + event.xml.results.toString());
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);
      newevent.response = new Object;
      var xml:XML = new XML(event.xml.results.artistmatches);
      newevent.response["artistmatches"] = xml;
      newevent.userData = userData;
      dispatchEvent(newevent);
    }

    private function onOkGetRecentTracks(event:RequestEvent):void
    {
      Debug.log("Api.onOkGetRecentTracks: results: " + event.xml.recenttracks.toString());
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);
      newevent.response = new Object;
      var xml:XML = new XML(event.xml.recenttracks);
      newevent.response.recenttracks = xml;
      newevent.userData = userData;
      dispatchEvent(newevent);
    }

    private function onOkGetBannedTracks(event:RequestEvent):void
    {
      Debug.log("Api.onOkGetBannedTracks: results: " + event.xml.bannedtracks.toString());
      var newevent:ApiEvent;
      if (null == event.xml.error)
      {
        newevent = new ApiEvent(ApiEvent.ERROR);
      }
      else
      {
        newevent = new ApiEvent(ApiEvent.OK);
        newevent.response = new Object;
        var xml:XML = new XML(event.xml.bannedtracks);
        newevent.response.bannedtracks = xml;
        newevent.userData = userData;
      }

      dispatchEvent(newevent);
    }

    private function onOkGetSession(event:RequestEvent):void
    {
      Debug.log("Api.onOkGetSession: results: " + event.xml.toString());
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);
      newevent.response = new Object;
      newevent.response["name"] = event.xml.session.name.toString();
      newevent.response["key"] = event.xml.session.key.toString();
      newevent.userData = userData;
      dispatchEvent(newevent);
    }

    private function onOkGetToken(event:RequestEvent):void
    {
      Debug.log("Api.onOkGetToken: results: " + event.xml.toString());
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);
      newevent.response = new Object;
      newevent.response["token"] = event.xml.token.toString();
      dispatchEvent(newevent);
    }

    ///////////////////////////////////////////////////////////////////////////

    private function onOkArtistInfo(event:RequestEvent):void
    {
      Debug.log("Api.onOkArtistInfo");
      var i:int; // iterator
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);
      newevent.response = new Object;
      newevent.response["artist"] = event.xml.artist.name.toString(); // TODO: sometimes null artist/name?
      newevent.response["url"] = event.xml.artist.url.toString();

      newevent.response.similar = new Array;
      var similar:XMLList = event.xml.artist.similar.artist;
      for (i = 0; i < similar.length(); ++i)
      {
        newevent.response.similar.push(similar[i].name.toString());
      }

      newevent.response["image"] = new Object;
      var image:XMLList = event.xml.artist.image;
      var str:String;
      for (i = 0; i < image.length(); ++i)
      {
        // Replace images with square ones.
        var type:String = image[i].@size.toString();
        var res:String = image[i].toString();
        if ("small" == type)
        {
          res = res.replace("/34/", "/34s/");
        }
        else if ("medium" == type)
        {
          res = res.replace("/64/", "/64s/");
        }
        else if ("large" == type)
        {
          res = res.replace("/126/", "/126s/");
        }
        else if ("extralarge" == type)
        {
          res = res.replace("/252/", "/252s/");
        }

        newevent.response["image"][type] = res;
      }

      newevent.response["stats"] = new Object;
      newevent.response["stats"]["listeners"] = event.xml.artist.stats.listeners.toString();
      newevent.response["stats"]["playcount"] = event.xml.artist.stats.playcount.toString();

      newevent.response["bio"] = event.xml.artist.bio.content.toString();
      newevent.userData = userData;

      dispatchEvent(newevent);
    }

    ///////////////////////////////////////////////////////////////////////////

    private function onOkTrack(event:RequestEvent):void
    {
      Debug.log("Api.onOkTrack: server response: " + event.xml);
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);

      newevent.response = new Object;
      newevent.response["artist"] = event.xml.toptracks.@artist.toString();
      newevent.response["track"] = event.xml.toptracks.track.name.toString();
      newevent.response["max"] = event.xml.toptracks.@total.toString();

      var i:int = -1;
      switch (event.xml.toptracks.track.image.length())
      {
        case 0:
          break;
        case 1:
          i = 0;
          break;
        case 2:
          i = 1;
          break;
        default:
          i = 1;
          break;
      }

      if (0 <= i)
      {
        newevent.response["image"] = event.xml.toptracks.track.image[i].toString();
      }

      newevent.userData = userData;
      dispatchEvent(newevent);
    }

    private function onOkGetTopTags(event:RequestEvent):void
    {
      Debug.log("Api.onOkGetTopTags");
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);
      newevent.response = new Object;
      var xml:XML = new XML(event.xml.toptags);
      newevent.response.toptags = xml;
      newevent.userData = userData;
      dispatchEvent(newevent);
    }

    private function onOkGetTopArtists(event:RequestEvent):void
    {
      Debug.log("Api.onOkGetTopArtists");
      var newevent:ApiEvent = new ApiEvent(ApiEvent.OK);
      newevent.response = new Object;
      var xml:XML = new XML(event.xml.artists);
      newevent.response.topartists = xml;
      newevent.userData = userData;
      dispatchEvent(newevent);
    }

    public function chartGetTopArtists():void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params.method = "chart.getTopArtists";

      request.addEventListener(RequestEvent.OK, onOkGetTopArtists);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    public function artistGetCorrection(name:String):void
    {
      Debug.log("Api.artistGetCorrection: " + name);
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params.method = "artist.getcorrection";
      params.artist = name;

      request.addEventListener(RequestEvent.OK, onOkCorrection);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    ///////////////////////////////////////////////////////////////////////////

    public function artistGetInfo(name:String):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["method"] = "artist.getinfo";
      params["artist"] = name;
      params["lang"] = "ru";

      request.addEventListener(RequestEvent.OK, onOkArtistInfo);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    ///////////////////////////////////////////////////////////////////////////

    public function artistGetSimilar(name:String):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["method"] = "artist.getsimilar";
      params["artist"] = name;
      params["autocorrect"] = 1;

      request.addEventListener(RequestEvent.OK, onOkSimilar);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    ///////////////////////////////////////////////////////////////////////////

    public function artistGetTrack(name:String, track:int):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["method"] = "artist.gettoptracks";
      params["artist"] = name;
      params["limit"] = 1;
      params["page"] = (0 < track ? track : 1);

      request.addEventListener(RequestEvent.OK, onOkTrack);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    public function artistSearch(str:String):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["method"] = "artist.search";
      params["artist"] = str;

      request.addEventListener(RequestEvent.OK, onOkArtistSearch);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    public function userGetBannedTracks(username:String, page:uint = 0, limit:uint = 0):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params.method = "user.getbannedtracks";
      params.user = username;
      if (0 < page)
      {
        params.page = page;
      }
      if (0 < limit)
      {
        params.limit = limit;
      }

      request.addEventListener(RequestEvent.OK, onOkGetBannedTracks);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    public function userGetRecentTracks(username:String, limit:uint = 0):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params.method = "user.getrecenttracks";
      params.user = username;
      if (0 < limit)
      {
        params.limit = limit;
      }

      request.addEventListener(RequestEvent.OK, onOkGetRecentTracks);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }

    public function authGetMobileSession(username:String, token:String):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["username"] = username;
      params["method"] = "auth.getMobileSession";
      params["authToken"] = token;

      request.addEventListener(RequestEvent.OK, onOkGetSession);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params, true);
    }

    public function authGetSession(token:String):void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["method"] = "auth.getSession";
      params["token"] = token;

      request.addEventListener(RequestEvent.OK, onOkGetSession);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params, true);
    }

    public function authGetToken():void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["method"] = "auth.getToken";

      request.addEventListener(RequestEvent.OK, onOkGetToken);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params, true);
    }

    public function trackUpdateNowPlaying(artist:String, track:String, duration:uint):void
    {
      var auth:lastfm.Auth = new lastfm.Auth;
      if (0 == auth.sessionKey.length)
      {
        return;
      }

      var params:URLVariables = new URLVariables;
      params.method = "track.updateNowPlaying";
      params.artist = artist;
      params.track = track;
      params.duration = duration;
      params.sk = auth.sessionKey;

      var request:Request = new Request;
      request.post(params);
    }

    public function trackScrobble(artist:String, track:String, timestamp:uint, duration:uint):void
    {
      var auth:lastfm.Auth = new lastfm.Auth;
      if (0 == auth.sessionKey.length)
      {
        return;
      }

      var params:URLVariables = new URLVariables;
      params.method = "track.scrobble";
      params.artist = artist;
      params.track = track;
      params.timestamp = timestamp;
      params.duration = duration;
      params.sk = auth.sessionKey;

      var request:Request = new Request;
      request.post(params);
    }

    public function trackLove(artist:String, track:String):void
    {
      var auth:lastfm.Auth = new lastfm.Auth;
      if (0 == auth.sessionKey.length)
      {
        return;
      }

      var params:URLVariables = new URLVariables;
      params.method = "track.love";
      params.artist = artist;
      params.track = track;
      params.sk = auth.sessionKey;

      var request:Request = new Request;
      request.post(params);
    }

    public function trackBan(artist:String, track:String):void
    {
      var auth:lastfm.Auth = new lastfm.Auth;
      if (0 == auth.sessionKey.length)
      {
        return;
      }

      var params:URLVariables = new URLVariables;
      params.method = "track.ban";
      params.artist = artist;
      params.track = track;
      params.sk = auth.sessionKey;

      var request:Request = new Request;
      request.post(params);
    }

    public function tagGetTopTags():void
    {
      var request:Request = new Request;
      var params:URLVariables = new URLVariables;
      params["method"] = "tag.getTopTags";

      request.addEventListener(RequestEvent.OK, onOkGetTopTags);
      request.addEventListener(RequestEvent.ERROR, onError);
      request.send(params);
    }
  }

}
